﻿using OperationPlugins.Compositions;
using OperationPlugins.Configurations;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace OperationPlugins
{
    public static class OperationPluginsEnvironment
    {
        private delegate CompositionContainer CreateCompostionContainer();

        /// <summary>
        /// Checks if the framework has already been initialized.
        /// </summary>
        public static bool IsInitialized { get; private set; }

        /// <summary>
        /// Checks if the framework is currently initializing.
        /// </summary>
        public static bool IsInitializing { get; private set; }

        /// <summary>
        /// Initializes the framework by using the configuration file.
        /// </summary>
        /// <remarks>
        /// This method can only be called once and should be called on the application start-up!
        /// </remarks>
        public static void InitializeByUsingConfigFile()
        {
            // Get the base directory, like D:\codeplex\operationplugins\Dev\OperationPlugins\OperationPlugins.WebApp\
            string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;

            // Read settings from the configuration file.
            CompositionSection settings = CompositionSection.Instance;
            if (settings == null)
            {
                throw new OperationPluginsEnvironmentException("Could not find the operationPluginsCompositions section in the configuration file.");
            }

            // Resolve composition assemblies.
            List<string> paths = new List<string>();
            foreach (AssemblyElement element in settings.Assemblies)
            {
                string path = baseDirectory + element.Path;
                if (File.Exists(path) == false)
                {
                    throw new OperationPluginsEnvironmentException(string.Format("Could not find the configured composition assembly {0}. Please check the configuration file.", path));                    
                }
                paths.Add(path);
            }

            Initialize(() =>
            {
                AggregateCatalog assemblies = new AggregateCatalog();
                foreach (string path in paths)
                {                  
                    assemblies.Catalogs.Add(new AssemblyCatalog(path));
                }
                return new CompositionContainer(assemblies, true);
            });
        }

        /// <summary>
        /// Initializes the framework by using specified composition assemblies.
        /// </summary>
        /// <param name="assemblies">Composition assemblies.</param>
        /// <remarks>
        /// This method can only be called once and should be called on the application start-up!
        /// </remarks>
        public static void InitializeByUsingAssemblies(params Assembly[] assemblies)
        {
            Debugger.Break();            
        }

        private static void Initialize(CreateCompostionContainer createCompostionContainer)
        {
            if (IsInitializing || IsInitialized)
            {
                throw new OperationPluginsEnvironmentException("The OperationPluginsEnvironment has already been initialized or is currently being initialized.");
            }

            IsInitializing = true;

            // Create the composition container.
            CompositionContainer container = createCompostionContainer();

            // Create a new initialization context.
            var context = new InitializationContext(container);

            // Initialize compositions by using the IInitialize interface.
            foreach (var plugin in container.GetPlugins<IInitialize>())
            {
                plugin.Initialize(context);
            }

            IsInitialized = true;
            IsInitializing = false;

            // ...and just after the initialization hook IInitialized implementations...
            foreach (var plugin in container.GetPlugins<IInitialized>())
            {
                plugin.Initialized(context);
            }            
        }
    }
}